// SyncHook,
// SyncBailHook,
// SyncWaterfallHook,
// SyncLoopHook,
// AsyncParallelHook,
// AsyncParallelBailHook, （no）
// AsyncSeriesHook,
// AsyncSeriesBailHook, （no）
// AsyncSeriesWaterfallHook （no）

// https://juejin.cn/post/6880156688112943112?searchId=20250919231920384D8F8CDBFBD5692E5D 【重要】
// https://juejin.cn/post/7040982789650382855?searchId=2025091923160328FACED8B22E31550BB0#heading-9 【辅助理解】


// SyncHook: 同步的、无返回值的钩子
// 串行同步执行
class SyncHook {
    constructor(instanseArgs) {
        this.instanseArgs = instanseArgs;
        this.tasks = [];
    }
    // taskName插件名称，只是起到注释作用
    tap(taskName, task) {
        this.tasks.push(task)
    }
    call(...args) {
        // 也可在参数不足时抛出异常
        if (args.length < this.instanseArgs.length) throw new Error("参数不足");

        // 传入参数严格对应创建实例传入数组中的规定的参数，执行时多余的参数为 undefined
        args = args.slice(0, this.instanseArgs.length);

        // 依次执行事件处理函数
        this.tasks.forEach(task => task(...args));
    }
}


// 创建实例
let syncHook = new SyncHook(["name", "age", 't']);
// let syncHook = new SyncHook(["name", "age", 't']); //会提示参数不足
// 注册事件
syncHook.tap("tast1", (name, age) => console.log("1", name, age));
syncHook.tap("tast2", (name, age) => console.log("2", name, age));
syncHook.tap("tast3", (name, age) => console.log("3", name, age));

// 触发事件，让监听函数执行
syncHook.call("hannie", 18);

// >
// 1 hannie 18
// 2 hannie 18
// 3 hannie 18



// SyncBailHook: 同步的、有返回值的钩子
// 串行同步执行，如果事件处理函数执行时有一个返回值不为空（即返回值不为 undefined），则跳过剩下未执行的事件处理函数

// 串行同步执行
class SyncBailHook {
    constructor(instanseArgs) {
        this.instanseArgs = instanseArgs;
        this.tasks = [];
    }
    // taskName插件名称，只是起到注释作用
    tap(taskName, task) {
        this.tasks.push(task)
    }
    call(...args) {
        // 也可在参数不足时抛出异常
        if (args.length < this.instanseArgs.length) throw new Error("参数不足");

        // 传入参数严格对应创建实例传入数组中的规定的参数，执行时多余的参数为 undefined
        args = args.slice(0, this.instanseArgs.length);

        // 依次执行事件处理函数
        let ret = undefined;
        for (let i = 0; i < this.tasks.length; i++) {
            ret = this.tasks[i](args)
            if (ret) {
                break;
            }
        }
        // forEach不能用break
        // this.tasks.forEach(function(task) { 
        //     ret = task(args)
        //     if(!ret){
        //       break;
        //     }

        // });

        // 依次执行事件处理函数，如果返回值不为空，则停止向下执行
        // let i = 0, ret;
        // do {
        //     ret = this.tasks[i++](...args);
        // } while (!ret);
    }

}

// 创建实例
let syncBailHook = new SyncBailHook(["name", "age"]);
// 注册事件
syncBailHook.tap("1", (name, age) => console.log("1", name, age));
syncBailHook.tap("2", (name, age) => {
    console.log("2", name, age);
    return "2";
});
// 跳过执行
syncBailHook.tap("3", (name, age) => console.log("3", name, age));
// 触发事件，让监听函数执行
syncBailHook.call("hannie", 18);

//   > 
//    1 hannie 18
//    2 hannie 18



// SyncLoopHook：串行循环执行
/**
 * 事件处理函数返回 true 表示继续循环，即循环执行当前事件处理函数，返回 undefined 表示结束循环
 * SyncBailHook 只决定是否继续向下执行后面的事件处理函数，而 SyncLoopHook 的循环是指循环执行每一个事件处理函数，直到返回 undefined 为止，才会继续向下执行其他事件处理函数，执行机制同理。
*/

class SyncLoopHook {
    constructor(args) {
        this.args = args;
        this.tasks = [];
    }
    tap(name, task) {
        this.tasks.push(task);
    }
    call(...args) {
        // 传入参数严格对应创建实例传入数组中的规定的参数，执行时多余的参数为 undefined
        args = args.slice(0, this.args.length);

        // 依次执行事件处理函数，如果返回值为 true，则继续执行当前事件处理函数
        // 直到返回 undefined，则继续向下执行其他事件处理函数
        this.tasks.forEach(task => {
            let ret;
            do {
                ret = task(...args);
            } while (ret === true || !(ret === undefined));
        });
    }
}



// 创建实例
let syncLoopHook = new SyncLoopHook(["name", "age"]);
// 定义辅助变量
let total1 = 0;
let total2 = 0;
// 注册事件
syncLoopHook.tap("1", (name, age) => {
    console.log("1", name, age, total1);
    return total1++ < 2 ? true : undefined;
});
syncLoopHook.tap("2", (name, age) => {
    console.log("2", name, age, total2);
    return total2++ < 2 ? true : undefined;
});
syncLoopHook.tap("3", (name, age) => console.log("3", name, age));
// 触发事件，让监听函数执行
syncLoopHook.call("hannie", 18);

// > 
// 1 hannie 18 0
// 1 hannie 18 1
// 1 hannie 18 2
// 2 hannie 18 0
// 2 hannie 18 1
// 2 hannie 18 2
// 3 hannie 18




// SyncWaterfallHook：串行同步执行
/**
 * SyncWaterfallHook 为串行同步执行，上一个事件处理函数的返回值作为参数传递给下一个事件处理函数，依次类推，正因如此，只有第一个事件处理函数的参数可以通过 call 传递，而 call 的返回值为最后一个事件处理函数的返回值
*/
// 串行同步执行
class SyncWaterfallHook {
    constructor(instanseArgs) {
        this.instanseArgs = instanseArgs;
        this.tasks = [];
    }
    // taskName插件名称，只是起到注释作用
    tap(taskName, task) {
        this.tasks.push(task)
    }
    call(...args) {
        // 也可在参数不足时抛出异常
        if (args.length < this.instanseArgs.length) throw new Error("参数不足");
        // 传入参数严格对应创建实例传入数组中的规定的参数，执行时多余的参数为 undefined
        args = args.slice(0, this.instanseArgs.length);

        let [first, ...others] = this.tasks;
        others.reduce((res, task) => {
            return task(res)
        }, first(...args))
    }

}

// 创建实例
let syncWaterfallHook = new SyncWaterfallHook(["name", "age"]);
// 注册事件
syncWaterfallHook.tap("1", (name, age) => {
    console.log("1", name, age);
    return "1";
});
syncWaterfallHook.tap("2", data => {
    console.log("2", data);
    return "2";
});
syncWaterfallHook.tap("3", data => {
    console.log("3", data);
    return "3"
});
// 触发事件，让监听函数执行
let ret = syncWaterfallHook.call("hannie", 18);
console.log("call", ret);

// >
// 1 hannie 18
// 2 1
// 3 2
// call undefined






// AsyncParallelHook（setTimeout）：并行异步执行

/**
 * 并行异步执行
*/
class AsyncParallelHook {
    constructor(instanseArgs) {
        this.instanseArgs = instanseArgs;
        this.tasks = [];
    }
    // taskName插件名称，只是起到注释作用
    tapAsync(taskName, task) {
        this.tasks.push(task)
    }
    callAsync(...args) {
        let finalCallback = args.pop();

        // 也可在参数不足时抛出异常
        if (args.length < this.instanseArgs.length) throw new Error("参数不足");

        // 传入参数严格对应创建实例传入数组中的规定的参数，执行时多余的参数为 undefined
        args = args.slice(0, this.instanseArgs.length); //["name", "age"]

        // let i = 0;
        // let done = () => {
        //     if (++i === this.tasks.length) {
        //         finalCallback();
        //     }
        // };
        // task是包含done回调函数的函数
        this.tasks.forEach((task, index) => {
            // task(...args,done)
            task(...args, () => {
                if (index === this.tasks.length) {
                    finalCallback();
                }
            })
            // if(index===this.tasks.length){
            //   finalCallback();
            // } //在此处也是可以的，就是为了循环完就执行
        })
    }

}

// 创建实例
let asyncParallelHook2 = new AsyncParallelHook(["name", "age"]);

// 注册事件
console.time("time");
asyncParallelHook2.tapAsync("1", (name, age, done) => {
    setTimeout(() => {
        console.log("1", name, age, new Date());
        done();
    }, 1000);
});
asyncParallelHook2.tapAsync("2", (name, age, done) => {
    setTimeout(() => {
        console.log("2", name, age, new Date());
        done();
    }, 2000);
});
asyncParallelHook2.tapAsync("3", (name, age, done) => {
    setTimeout(() => {
        console.log("3", name, age, new Date());
        done();
        console.timeEnd("time");
    }, 3000);
});
// 触发事件，让监听函数执行
asyncParallelHook2.callAsync("hannie", 18, () => {
    console.log("complete");
});


// >
// 1 hannie 18 2020-08-03T06:13:14.007Z
// 2 hannie 18 2020-08-03T06:13:15.001Z
// 3 hannie 18 2020-08-03T06:13:16.004Z
// time: 3005.195ms









/**
 * 并行异步执行
*/

class AsyncParallelHook {
    constructor(instanseArgs) {
        this.instanseArgs = instanseArgs;
        this.tasks = [];
    }
    // taskName插件名称，只是起到注释作用
    tapPromise(taskName, task) {
        this.tasks.push(task)
    }
    promise(...args) {
        // 传入参数严格对应创建实例传入数组中的规定的参数，执行时多余的参数为 undefined
        args = args.slice(0, this.instanseArgs.length); //["name", "age"]
        return Promise.all(this.tasks.map((task) => {
            return task(...args)
        }))
        // return Promise.all(this.tasks.map(task => task(...args)));
    }
}


// 创建实例
let asyncParallelHook = new AsyncParallelHook(["name", "age"]);

// 注册事件
console.time("time");
asyncParallelHook.tapPromise("1", (name, age) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("1", name, age, new Date());
            resolve("1");
        }, 1000);
    });
});
asyncParallelHook.tapPromise("2", (name, age) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("2", name, age, new Date());
            resolve("2");
        }, 2000);
    });
});
asyncParallelHook.tapPromise("3", (name, age) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("3", name, age, new Date());
            resolve("3");
            console.timeEnd("time");
        }, 3000);
    });
});
// 触发事件，让监听函数执行
asyncParallelHook.promise("hannie", 18).then(ret => {
    console.log(ret);
});

// >
// 1 hannie 18 2020-08-03T06:22:47.198Z
// 2 hannie 18 2020-08-03T06:22:48.198Z
// 3 hannie 18 2020-08-03T06:22:49.197Z
// time: 3004.218ms
// [ '1', '2', '3' ]




/**
 * 串行异步执行
 * 一步一步执行
*/
class AsyncSeriesHook {
    constructor(instanseArgs) {
        this.instanseArgs = instanseArgs;
        this.tasks = [];
    }
    // taskName插件名称，只是起到注释作用
    tapAsync(taskName, task) {
        this.tasks.push(task)
    }
    callAsync(...args) {
        let finalCallback = args.pop();
        // 传入参数严格对应创建实例传入数组中的规定的参数，执行时多余的参数为 undefined
        args = args.slice(0, this.instanseArgs.length); //["name", "age"]

        // 递归next
        let i = 0;
        let next = () => {
            let task = this.tasks[i++];
            task ? task(...args, next) : finalCallback();
        }
        next()
        // this.tasks.forEach((task,index)=>{
        //   task(...args, next)
        // })
    }
}

// 创建实例
let asyncSeriesHook3 = new AsyncSeriesHook(["name", "age"]);

// 注册事件
console.time("time");
asyncSeriesHook3.tapAsync("1", (name, age, next) => {
    setTimeout(() => {
        console.log("1", name, age, new Date());
        next();
    }, 1000);
});
asyncSeriesHook3.tapAsync("2", (name, age, next) => {
    setTimeout(() => {
        console.log("2", name, age, new Date());
        next();
    }, 2000);
});
asyncSeriesHook3.tapAsync("3", (name, age, next) => {
    setTimeout(() => {
        console.log("3", name, age, new Date());
        next();
        console.timeEnd("time");
    }, 3000);
});
// 触发事件，让监听函数执行
asyncSeriesHook3.callAsync("hannie", 18, () => {
    console.log("complete");
});

// > 
// 1 hannie 18 2020-08-03T06:32:51.094Z
// 2 hannie 18 2020-08-03T06:32:53.103Z
// 3 hannie 18 2020-08-03T06:32:56.106Z
// complete
// time: 6015.353ms


/**
 * 串行异步执行
*/

class AsyncSeriesHook{
	constructor(instanseArgs){
		this.instanseArgs = instanseArgs;
		this.tasks = [];
	}
    // taskName插件名称，只是起到注释作用
    tapPromise(taskName,task){
		this.tasks.push(task)
	}
	promise(...args){
		// 传入参数严格对应创建实例传入数组中的规定的参数，执行时多余的参数为 undefined
		args = args.slice(0, this.instanseArgs.length); //["name", "age"]
        let [first, ...others] = this.tasks;
        return others.reduce((promise, task) => {    
        return promise.then(() => task(...args));
        }, first(...args));
	}
}

// 创建实例
let asyncSeriesHook = new AsyncSeriesHook(["name", "age"]);
// 注册事件
console.time("time");
asyncSeriesHook.tapPromise("1", (name, age) => {
    console.log( name, age);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("1", name, age, new Date());
            resolve("1");
        }, 1000);
    });
});
asyncSeriesHook.tapPromise("2", (name, age) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("2", name, age, new Date());
            resolve("2");
        }, 2000);
    });
});
asyncSeriesHook.tapPromise("3", (name, age) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("3", name, age, new Date());
            resolve("3");
            console.timeEnd("time");
        }, 3000);
    });
});
// 触发事件，让监听函数执行
asyncSeriesHook.promise("hannie", 18).then(ret => {
    console.log(ret);
});

// > 
// 1 hannie 18 2020-08-03T06:42:05.558Z
// 2 hannie 18 2020-08-03T06:42:07.565Z
// 3 hannie 18 2020-08-03T06:42:10.567Z
// time: 6016.385ms
// 3